<?php

/*
    This file is part of TFCMS.

    TFCMS is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    TFCMS is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with TFCMS. If not, see <http://www.gnu.org/licenses/>.
*/

include "htmlelement.class.php";

abstract class TFCMSAbstractView
{
	abstract function addJavaScript($path);
	abstract function addMetaData($name, $content);
	abstract function addStyleSheet($path);
	abstract function currentPageTitle();
	abstract function parseTemplate();
	abstract function renderBreadcrumbs();
	abstract function renderClosingJavaScript(TFCMSCategory &$data);
	abstract function renderContent();
	abstract function renderDocType();
	abstract function renderFavicon();
	abstract function renderJavaScript($complete = false);
	abstract function renderMetaData();
	abstract function renderNavigation();
	abstract function renderStyleSheet($complete = false);
	abstract function setDocType($doctype);
	abstract function setModel(TFCMSAbstractModel &$model);
}

/**
 *	\class TFCMSView
 *	\file view_tfcms.class.php
 *	\brief TFCMS View class.
 *
 *	TFCMSView is used mostly to render all the data for the browser.
 */

class TFCMSView extends TFCMSAbstractView
	{
		private $current_file_path = NULL; ///< Current page cache.
		private $doctype = NULL; ///< Holds DOCTYPE.
		private $fallback_template = NULL;
		private $favicon = NULL;
		private $meta_data = array(); ///< Holds metadata.
		private $model = NULL; ///< Reference to TFCMSModel object.
		private $javascripts = array(); ///< Extra JavaScript files.
		private $javascript_path = "/script.js"; ///< Full path of the JavaScript of desired template.
		private $nav_title_class = "navTitle"; ///< Value for CSS class of root item titles in the navigation.
		private $root_nav_class = "Nav"; ///< Value for CSS class of root items in the navigation.
		private $stylesheet_path = "/style.css"; ///< Full path of the stylesheet of desired template.
		private $stylesheets = array(); ///< Extra stylesheets.
		private $sub_nav_class = "subNav"; ///< Value for CSS class of sub items in the navigation.
		private $sub_nav_title_class = "navSubTitle"; ///< Value for CSS class of sub item titles in the navigation.
		private $template = NULL; ///< Template name.
		private $template_path = NULL; ///< Full path of desired template.
		private $tpl_file = NULL; ///< The file specific to the body content.

		const ERROR_404 = "<p id=\"error_404\">The page you have requested could not be found.</p>";

		/** \brief TFCMSView constructor.
		 *
		 *	\note
		 *
		 *	If full path is not provided for any of the parameters,
		 *	 then the system will assume __cwd."/templates/$template".
		 *	You can dynamically change the template by setting the GET
		 *	 \em tfcms_template variable in the browser URL to the name
		 *	 of an existing template.
		 *
		 *	@param string $template Name or path of desired template directory.
		 *	@param string $default Name or path of fallback template directory.
		 *	@param string $doctype What documentation type to use.
		 *	@param string $tpl_file Name of file to use for the template.
		 *
		 *	\todo
		 *
		 *	Implement a template config parser to replace $doctype and $tpl_file param
		 */
		function __construct($template = "Default", $default = "Default", $doctype = "XHTML 1.1", $tpl_file = "index.tpl.php")
			{
				$this->doctype = $doctype;
				$this->tpl_file = $tpl_file;

				if (isset($_GET['tfcms_template']))
				{
					if (preg_match("#^[\w\d\-]+$#", $_GET['tfcms_template']))
						$template = $_GET['tfcms_template'];
				}

				$this->template = $template;
				$this->fallback_template = $default;
			}

		/** \brief Add JavaScript for inclusion.
		 *
		 *	This method is called internally by TFCMS::addGlobalJavaScripts()
		 *	 and therefore should not be called directly.
		 *
		 *	\note
		 *
		 *	The system will use the "script.js" file located at the
		 *	 desired template's path if any and it will use the one
		 *	 located at __root."/base.js" if any.
		 *
		 *	@param string $path Path to the JavaScript file.
		 *
		 *	@see TFCMS::addGlobalJavaScripts()
		 */
		function addJavaScript($path)
			{
				$path = realpath($path);
				$this->javascripts[] = $path;
			}

		/** \brief Add metadata for rendering.
		 *
		 *	This method is called internally by TFCMS::addMetaData()
		 *	 and therefore should not be called directly.
		 */
		function addMetaData($name, $content)
			{
				$this->meta_data[$name] = $content;
			}

		/** \brief Add stylesheet for inclusion.
		 *
		 *	This method is called internally by TFCMS::addGlobalStyleSheets()
		 *	 and therefore should not be called directly.
		 *
		 * 	\note
		 *
		 *	The system will use the "style.css" file located at the
		 *	 desired template's path if any and it will use the one
		 *	 located at __root."/base.css" if any.
		 *
		 *	@param string $path Path to the stylesheet file.
		 *
		 *	@see TFCMS::addGlobalStyleSheets()
		 */
		function addStyleSheet($path)
			{
				$path = realpath($path);
				$this->stylesheets[] = $path;
			}

		/** \brief Make category ID suitable for URLs
		 *
		 *	@param string $category ID of category.
		 */
		protected function categoryToUrl($category)
			{
				$category = str_replace(" ", "_", strtolower($category));
				return $_SERVER['SCRIPT_NAME']."/".$category;
			}

		/** \brief Translate current file path to title and return it.
		 */
		function currentPageTitle()
			{
				if (empty($this->current_file_path))
					$this->current_file_path = $this->model->getCurrentFilePath();

				if (!empty($this->current_file_path))
				{
					$current_page = pathinfo($this->current_file_path);
					$current_page = str_replace("_", " ", $current_page['filename']);
					$current_page = ucwords($current_page);
				}

				else
					$current_page = $this->model->currentCategory()->name();

				return ($this->current_file_path != $this->model->getHomePage()) ? $current_page : "Home";
			}

		/**	\brief Get the path of the JavaScript script specific to the template.
		 *
		 *	@param bool $full_path Whether or not to return full system path or path relative to the domain.
		 */
		protected function getJavaScriptPath($full_path = false)
			{
				return ($full_path)
					? realpath($this->template_path."/".$this->javascript_path)
					: str_replace(__root, "", $this->template_path . $this->javascript_path);
			}

		/*! \brief Get the path of the stylesheet specific to the template.
		 *
		 *	@param bool $full_path Whether or not to return full system path or path relative to the domain.
		 */
		protected function getStyleSheetPath($full_path = false)
			{
				if ($full_path)
					return $this->template_path . $this->stylesheet_path;
				else
					return str_replace(__root, "", $this->template_path . $this->stylesheet_path);
			}

		/** \brief Figure out what template to use.
		 *
		 *	@see renderStyleSheet()
		 */
		protected function getTemplatePath()
			{
				$curr_category = $this->model->currentCategory();
				$default_category = $this->model->getDefaultCategory();

				if ($curr_category != null)
				{
					do
					{
						if ($curr_category['template'] != null)
						{
							$this->template = $curr_category['template'];
							break;
						}
					}
					while ($curr_category = $curr_category['parent']);
				}

				else if ($default_category != null)
				{
					do
					{
						if ($default_category['template'] != null)
						{
							$this->template = $default_category['template'];
							break;
						}
					}
					while ($default_category = $default_category['parent']);
				}

				if (file_exists($this->template))
					$this->template_path = realpath($this->template);
				else if (!empty($this->template))
					$this->template_path = realpath(__cwd."/templates/".$this->template);
				else
				{
					if (file_exists($this->fallback_template))
						$this->template_path = realpath($this->fallback_template);
					else
						$this->template_path = realpath(__cwd."/templates/".$this->fallback_template);
				}
			}

		/** \brief Translate file path to title and return it.
		 *
		 *	This is a convenience function used for rendering the navigation.
		 *
		 *	@param string $file Path to file.
		 *
		 *	@see renderNavigation()
		 *	@see renderDirectories()
		 */
		protected function filePathToName($file)
			{
				$file = pathinfo($file);
				$file = str_replace("_", " ", $file['filename']);
				return ucwords($file);
			}

		/** \brief Translate file path to URL and return it.
		 *
		 *	This is a convenience function used for rendering the navigation.
		 *
		 *	@param string $file Path to file.
		 *	@param string $category Category ID.
		 *
		 *	@see renderNavigation()
		 *	@see renderDirectories()
		 */
		protected function filePathToUrl($file, $category)
			{
				$file = pathinfo($file);
				$file = $file['filename'];
				$file = self::removeSpecialCharacters($file);
				$category = $this->parseCatForHtml($category);
				$file = $_SERVER['SCRIPT_NAME']."/".$category."/".$file;
				return $file;
			}

		/** \brief Translate category ID for use in HTML and return it.
		 *
		 *	This is a convenience function used for rendering the navigation.
		 *
		 *	@see renderNavigation()
		 */
		protected function parseCatForHtml($cat)
			{
				$cat = strtolower($cat);
				$cat = str_replace(" ", "_", $cat);
				$cat = self::removeSpecialCharacters($cat);
				return $cat;
			}

		/** \brief Include template file.
		 */
		function parseTemplate()
			{
				$this->getTemplatePath();

				if (file_exists($this->template_path."/".$this->tpl_file))
					include $this->template_path."/".$this->tpl_file;
				else
					echo "<b>Error:</b> template file, ".$this->tpl_file.", does not exist.";
			}

		/** \brief Remove special characters from \p $text
		 *
		 *	@param string $text Text to be filtered.
		 */
		static function removeSpecialCharacters($text)
			{
				return preg_replace("#[^\w\d]#", "", $text);
			}

		function renderBreadcrumbs($default = false, $separator = " &raquo; ")
			{
				$breadcrumbs = $this->model->getBreadcrumbs();

				if (count(explode("/", TFCMS_PATH)) > 1)
				{
					$last = pathinfo($breadcrumbs[count($breadcrumbs)-1]);
					$last = $last['filename'];
					$last = ucwords(str_replace("_", " ", $last));
					$breadcrumbs[count($breadcrumbs)-1] = $last;
				}

				else
				{
					unset($breadcrumbs[count($breadcrumbs)-1]);
				}

				$breadcrumbs = array_unique($breadcrumbs);

				foreach ($breadcrumbs as $key => $value)
				{
					$crumb = $this->model->getCategoryById($breadcrumbs[$key]);

					if ($crumb != null)
						$breadcrumbs[$key] = $crumb['name'];
				}

				if ($default && !count($breadcrumbs))
					echo $this->model->getDefaultCategory()->name();
				else
					echo join($separator, $breadcrumbs);
			}

		function renderCategory(TFCMSCategory &$data, HtmlElement &$root_elem, $render_subs = true, $force_links = false, $render_ids = true)
			{
				$breadcrumbs = $this->model->getBreadcrumbs();
				$active = (in_array($data['id'], $breadcrumbs)) ? "active" : "";

				$li_elem = new HtmlElement("li", $root_elem);

				if ($active != "")
					$li_elem->setAttribute("class", @$active);

				$a_elem = new HtmlElement("a", $li_elem);

				$a_elem->setContext($data['name'])
					   ->setAttribute("class", $this->nav_title_class);

				if ($force_links && !$data->hasUrl())
					$data['url'] = $this->filePathToUrl("", $data['id']);

				if ($data->hasUrl())
					$a_elem->setAttribute("href", $data['url']);

				else if ($data->hasFile())
					$a_elem->setAttribute("href", $this->categoryToUrl($data['id']));

				else $a_elem->setElementName("span");

				$has_subs = (bool) ($data->hasChildren() || $data->hasDirectories() || $data->hasLinks());

				if ($has_subs && $render_subs)
				{
					$ul_elem = new HtmlElement("ul", $li_elem);
					$ul_elem->setAttribute("class", $this->sub_nav_class);

					if ($render_ids)
						$ul_elem->setAttribute("id", strtolower(str_replace(" ", "_", $data['id'])));

					if ($data->hasChildren())
					{
						$li_elem = new HtmlElement("li", $ul_elem);
						$this->renderSubCategory($data, $li_elem, $render_ids);
					}

					if ($data->hasDirectories())
						$this->renderDirectories($data, $ul_elem);

					if ($data->hasLinks())
						$this->renderLinks($data, $ul_elem);
				}

				return $root_elem;
			}

		/** \brief Render category-specific JavaScript for the end of the page.
		 *
		 *	@param TFCMSCategory $data Category to retrieve JavaScript paths from.
		 */
		function renderClosingJavaScript(TFCMSCategory &$data)
			{
				if ($data->hasJavascripts())
				{
					foreach ($data['js'] as $script_path)
						echo "<script type=\"text/javascript\" src=\"$script_path\"></script>\n";
				}

				if ($data['parent'] != null)
					$this->renderClosingJavaScript($data['parent']);
			}

		/** \brief Render the current page.
		 *
		 *	Will render the current page based on the URL.
		 *	 If the URL does not point to a file, it will attempt to
		 *	 render category-related information from the current
		 *	 category if $page_previews equals \em true. If no
		 *	 category-related data is found and $page_preview equals
		 *	 \em true, it will simply render the category. If the page
		 *	 does not exist, an error message is displayed.
		 *
		 *	If $preview_limit equals -1, it will attempt to render a
		 *	 preview for all pages found in each directory.
		 *
		 *	\note
		 *
		 *	When displaying the page preview, all HTML is stripped from
		 *	 the file.
		 *
		 *	\warning
		 *
		 *	When using page previews, this method does not strip any
		 *	 PHP code from the files.
		 *
		 *	@param bool $page_previews Whether or not to show a preview of the first-listed file in each directory.
		 *	@param int $preview_limit Limit to how many files should be dislpayed per directory.
		 *	@param int $max_chars Limit to how many characters from the files to show.
		 */
		function renderContent($page_previews = false, $preview_limit = 1, $max_chars = 300)
			{
				$this->current_file_path = $this->model->getCurrentFilePath();

				if (file_exists($this->current_file_path))
					include $this->current_file_path;

				else
				{
					if (isset($this->current_file_path))
						echo self::ERROR_404;

					$curr_category = $this->model->currentCategory();

					if ($curr_category != null)
					{
						if ($page_previews)
						{
							$has = false;

							if ($curr_category->hasChildren())
							{
								$has = true;

								foreach ($curr_category['subs'] as $child)
								{
									if ($child->hasDirectories())
									{
										foreach ($child['dirs'] as $dir)
										{
											if ($dir->hasFiles())
											{
												if ($preview_limit < 0)
													$limit = count($dir['files']);
												else
													$limit = $preview_limit;

												for ($i = 0; $i < $limit; ++$i)
												{
													$this->renderPagePreview($dir, $dir['files'][$i], $max_chars);
												}
											}
										}
									}
								}
							}

							if ($curr_category->hasDirectories())
							{
								$has = true;

								foreach ($curr_category['dirs'] as $dir)
								{
									if ($dir->hasFiles())
									{
										if ($preview_limit < 0)
											$limit = count($dir['files']);
										else
											$limit = $preview_limit;

										for ($i = 0; $i < $limit; ++$i)
										{
											$this->renderPagePreview($dir, $dir['files'][$i], $max_chars);
										}
									}
								}
							}

							if (!$has)
								$this->renderCategory($curr_category);
						}

						else
							$this->renderCategory($curr_category);
					}

					else
						echo self::ERROR_404;
				}
			}

		protected function renderDirectories(TFCMSCategory &$data, HtmlElement &$elem)
			{
				// $ul_elem = new HtmlElement("ul");
				// $elem->addChildElement($ul_elem);

				foreach ($data['dirs'] as $dir)
				{
					foreach ($dir->files() as $file)
					{
						$li_elem = new HtmlElement("li");
						$elem->addChildElement($li_elem);

						$a_elem = new HtmlElement("a");
						$li_elem->addChildElement($a_elem);

						$a_elem->setAttribute("href", $this->filePathToUrl($file, $data['id']))
							   ->setContext($this->filePathToName($file));
					}
				}
			}

		function renderDocType()
			{
				switch($this->doctype)
				{
					case "HTML 4.01 Frameset":
						echo "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">\n";
						echo "<html>\n";
						break;
					case "HTML 4.01 Strict":
						echo "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">\n";
						echo "<html>\n";
						break;
					case "HTML 4.01 Transitional":
						echo "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n";
						echo "<html>\n";
						break;
					case "XHTML 1.0 Frameset":
						echo "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Frameset//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd\">\n";
						echo "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n";
						break;
					case "XHTML 1.0 Strict":
						echo "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n";
						echo "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n";
						break;
					case "XHTML 1.0 Transitional":
						echo "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n";
						echo "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n";
						break;
					default: // XHTML 1.1
						echo "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\" \"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">\n";
						echo "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\">\n";
						break;
				}
			}

		function renderFavicon()
			{
				if (!empty($this->favicon))
				{
					$image = getimagesize($this->favicon);
					echo "<link rel=\"icon\" type=\"".$image['mime']."\" href=\"".str_replace(__root, "", $this->favicon)."\" />\n";
				}
			}

		/*! \brief Renders global JavaScript to the page.
		 *
		 *	@param bool $complete Whether or not to include the contents of the JavaScript file instead of linking to it.
		 */
		function renderJavaScript($complete = false)
			{
				foreach ($this->javascripts as $script)
				{
					echo "<script type=\"text/javascript\"";
					if ($complete)
					{
						echo ">\n";
						echo file_get_contents($script);
					}

					else
						echo " src=\"".str_replace(__root, "", $script)."\">";
					echo "</script>\n";
				}

				// Generic
				if (file_exists(__root."/base.js"))
					echo "<script type=\"text/javascript\" src=\"/base.js\"></script>\n";

				// Template specific
				if (file_exists($this->template_path."/".$this->javascript_path))
				{
					echo "<script type=\"text/javascript\"";
					if ($complete)
					{
						echo ">\n";
						echo file_get_contents($this->template_path."/".$this->javascript_path);
					}

					else
						echo " src=\"".$this->getJavaScriptPath()."\">";
					echo "</script>\n";
				}
			}

		protected function renderLinks(TFCMSCategory &$data, HtmlElement &$elem)
			{
				// $ul_elem = new HtmlElement("ul");
				// $elem->addChildElement($ul_elem);

				foreach ($data['links'] as $link)
				{
					$li_elem = new HtmlElement("li");
					$elem->addChildElement($li_elem);

					$a_elem = new HtmlElement("a");
					$li_elem->addChildElement($a_elem);

					$link = explode("=", $link);
					$a_elem->setAttribute("href", $link[1])
						   ->setContext($link[0]);
				}
			}

		function renderMetaData()
			{
				echo "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\n";
				foreach ($this->meta_data as $name => $content)
					echo "<meta name=\"$name\" content=\"$content\" />\n";
			}

		function renderNavigation($render_subs = true, $force_links = false)
			{
				$root_cats = $this->model->getRootCategories();

				if (count($root_cats))
				{
					$root_elem = new HtmlElement("ul");
					$root_elem->setIndent(1);

					// Set ul.Nav
					$root_elem->setAttribute("class", $this->root_nav_class);

					foreach ($root_cats as $data)
					{
						$this->renderCategory($data, $root_elem, $render_subs, $force_links);
					}

					echo $root_elem;
				}
			}

		protected function renderPagePreview(TFCMSDir $dir, $file, $max_chars)
			{
				echo "<h3 class=\"page_preview_header\">".$this->filePathToName($file)."</h3>\n";
				echo "<p class=\"page_preview\">\n";
				echo strip_tags(file_get_contents($file, false, null, 0, $max_chars))." ...";
				echo "<a class=\"read_more\" href=\"".$this->filePathToUrl($file, $dir['category'])."\">read more</a>\n";
				echo "</p>\n";
			}

		/*! \brief Renders stylesheet-related information for browser.
		 *
		 *	@param bool $complete Whether or not to include the contents of the CSS file instead of linking to it.
		 */
		function renderStyleSheet($complete = false)
			{
				$this->getTemplatePath();

				if (file_exists(__cwd."/base.css"))
				{
					$path = str_replace(__root, "", __cwd);
					echo "<link rel=\"stylesheet\" type=\"text/css\" href=\"$path/base.css\" />\n";
				}

				else if (file_exists(__root."/base.css"))
					echo "<link rel=\"stylesheet\" type=\"text/css\" href=\"/base.css\" />\n";

				// GLobal stylesheets
				foreach ($this->stylesheets as $stylesheet)
				{
					if (file_exists($stylesheet))
					{
						if ($complete)
						{
							echo "<style type=\"text/css\">";
							echo file_get_contents($stylesheet);
							echo "</style>";
						}

						else
							echo "<link rel=\"stylesheet\" type=\"text/css\" href=\"".str_replace(__root, "", $stylesheet)."\" />\n";
					}
				}

				$stylesheet_path = $this->getStyleSheetPath(true);
				if (file_exists($stylesheet_path))
				{
					if ($complete)
					{
						echo "<style type=\"text/css\">\n";
						echo file_get_contents($stylesheet_path);
						echo "</style>\n";
					}

					else
						echo "<link rel=\"stylesheet\" type=\"text/css\" href=\"".$this->getStyleSheetPath()."\" />\n";
				}
			}

		/*! \brief Render sub (non-root) categories
		 *
		 *	@param TFCMSCategory $data The parent category to retrieve the sub-categories from.
		 *	@param string $tab How many tabs to prefix for the rendered HTML.
		 *
		 */
		protected function renderSubCategory(TFCMSCategory &$data, HtmlElement &$elem, $render_ids = true)
			{
				foreach ($data['subs'] as $sub)
				{
					// $elem->parent()->setAttribute("class", $this->sub_nav_class);

					$a_elem = new HtmlElement("a", $elem);
					$a_elem->setAttribute("class", $this->sub_nav_title_class)
						   ->setContext($sub['name']);

					$ul_elem = new HtmlElement("ul", $elem);

					if ($render_ids)
						$ul_elem->setAttribute("id", $this->parseCatForHtml($sub['id']));

					// $li_elem = new HtmlElement("li");
					// $ul_elem->addChildElement($li_elem);

					if ($sub->hasUrl())
						$a_elem->setAttribute("href", $sub['url']);

					else if ($sub->hasFile())
						$a_elem->setAttribute("href", $this->categoryToUrl($sub['id']));

					else $a_elem->setElementName("span");

					if ($sub->hasChildren())
						$this->renderSubCategories($sub, $ul_elem);

					if ($sub->hasDirectories())
						$this->renderDirectories($sub, $ul_elem);

					if ($sub->hasLinks())
						$this->renderLinks($sub, $ul_elem);
				}
			}

		/*!	\brief Set the DOCTYPE of the document.
		 *
		 *	Used to determine which standard to render HTML for.
		 *
		 *	Possible values:
		 *	 "HTML 4.01 Frameset",
		 *	 "HTML 4.01 Strict",
		 *	 "HTML 4.01 Transitional",
		 *	 "XHTML 1.0 Frameset",
		 *	 "XHTML 1.0 Strict",
		 *	 "XHTML 1.0 Transitional",
		 *	 "XHTML 1.1".
		 *
		 *	\note
		 *
		 *	Default DOCTYPE is "XHTML 1.1".
		 *
		 *	@param string $doctype DOCTYPE
		 */
		function setDocType($doctype)
			{
				$this->doctype = $doctype;
			}

		function setFavicon($favicon)
			{
				$this->favicon = realpath($favicon);
			}

		/** \brief Set the view's model.
		 *
		 *	If you want the view to make use of another model, set the model with this method.
		 *
		 *	@param TFCMSAbstractModel $model Reference to a TFCMSAbstractModel sub class.
		 */
		function setModel(TFCMSAbstractModel &$model)
			{
				$this->model = $model;
			}
	}

?>
